package cn.hamster3.mc.plugin.ball.bukkit.listener;

import cn.hamster3.mc.plugin.ball.bukkit.HamsterBallPlugin;
import cn.hamster3.mc.plugin.ball.bukkit.data.BukkitLocation;
import cn.hamster3.mc.plugin.ball.common.api.BallAPI;
import cn.hamster3.mc.plugin.ball.common.entity.BallServerType;
import cn.hamster3.mc.plugin.ball.common.event.operate.*;
import cn.hamster3.mc.plugin.core.common.api.CoreAPI;
import cn.hamster3.mc.plugin.core.common.data.DisplayMessage;
import com.google.common.eventbus.Subscribe;
import lombok.AllArgsConstructor;
import lombok.Data;
import net.kyori.adventure.audience.Audience;
import net.kyori.adventure.text.TextReplacementConfig;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerTeleportEvent;
import org.jetbrains.annotations.NotNull;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;
import java.util.logging.Level;

public class BallBukkitListener implements Listener {
    public static final BallBukkitListener INSTANCE = new BallBukkitListener();

    private final HashMap<UUID, ToLocation> playerToLocation = new HashMap<>();
    private final HashMap<UUID, ToPlayer> playerToPlayer = new HashMap<>();

    private BallBukkitListener() {
    }

    @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGH)
    public void onPlayerJoin(PlayerJoinEvent event) {
        Player player = event.getPlayer();
        UUID uuid = player.getUniqueId();
        ToLocation toLocationData = playerToLocation.remove(uuid);
        if (toLocationData != null) {
            player.teleport(toLocationData.getLocation(), PlayerTeleportEvent.TeleportCause.PLUGIN);
            DisplayMessage doneMessage = toLocationData.getDoneMessage();
            if (doneMessage != null) {
                Audience audience = CoreAPI.getInstance().getAudienceProvider().player(player.getUniqueId());
                doneMessage.show(audience);
            }
        }
        ToPlayer toPlayerData = playerToPlayer.remove(uuid);
        if (toPlayerData != null) {
            player.teleport(toPlayerData.location);
            if (toPlayerData.doneMessage != null) {
                Audience audience = CoreAPI.getInstance().getAudienceProvider().player(player.getUniqueId());
                toPlayerData.doneMessage.show(audience, TextReplacementConfig.builder()
                        .matchLiteral("%player_name%")
                        .replacement(BallAPI.getInstance().getPlayerName(toPlayerData.toPlayerUUID, ""))
                        .build());
            }
            if (toPlayerData.doneTargetMessage != null) {
                Audience audience = CoreAPI.getInstance().getAudienceProvider().player(toPlayerData.toPlayerUUID);
                toPlayerData.doneTargetMessage.show(audience, TextReplacementConfig.builder()
                        .matchLiteral("%player_name%")
                        .replacement(player.getName())
                        .build());
            }
        }
        HamsterBallPlugin.async(() -> {
            try {
                List<DisplayMessage> cachedPlayerMessage = getCachedPlayerMessage(uuid);
                Audience audience = CoreAPI.getInstance().getAudienceProvider().player(uuid);
                for (DisplayMessage message : cachedPlayerMessage) {
                    message.show(audience);
                }
                removeCachedPlayerMessage(uuid);
            } catch (SQLException e) {
                BallAPI.getInstance().getLogger().log(Level.SEVERE, "发送玩家缓存消息时出现了一个异常", e);
            }
        });
    }

    @Subscribe
    public void onBroadcastPlayerMessage(BroadcastPlayerMessageEvent event) {
        DisplayMessage message = event.getMessage();
        Audience audience = CoreAPI.getInstance().getAudienceProvider().all();
        message.show(audience);
    }

    @Subscribe
    public void onDispatchConsoleCommand(DispatchConsoleCommandEvent event) {
        if (event.getType() != null && event.getType() != BallServerType.GAME) {
            return;
        }
        if (event.getServerID() != null && !BallAPI.getInstance().isLocalServer(event.getServerID())) {
            return;
        }
        HamsterBallPlugin.sync(() -> Bukkit.dispatchCommand(Bukkit.getConsoleSender(), event.getCommand()));
    }

    @Subscribe
    public void onDispatchPlayerCommand(DispatchPlayerCommandEvent event) {
        if (event.getType() != null && event.getType() != BallServerType.GAME) {
            return;
        }
        if (event.getUuid() != null) {
            Player player = Bukkit.getPlayer(event.getUuid());
            if (player == null) {
                return;
            }
            HamsterBallPlugin.sync(() -> Bukkit.dispatchCommand(player, event.getCommand()));
            return;
        }
        for (Player player : Bukkit.getOnlinePlayers()) {
            HamsterBallPlugin.sync(() -> Bukkit.dispatchCommand(player, event.getCommand()));
        }
    }

    @Subscribe
    public void onSendPlayerToLocation(SendPlayerToLocationEvent event) {
        BukkitLocation location = new BukkitLocation(event.getLocation());
        if (!BallAPI.getInstance().isLocalServer(location.getServerID())) {
            return;
        }
        for (UUID uuid : event.getSendPlayerUUID()) {
            Player player = Bukkit.getPlayer(uuid);
            if (player != null) {
                HamsterBallPlugin.sync(() -> {
                    player.teleport(location.toBukkitLocation());
                    if (event.getDoneMessage() != null) {
                        Audience audience = CoreAPI.getInstance().getAudienceProvider().player(player.getUniqueId());
                        event.getDoneMessage().show(audience);
                    }
                });
            } else {
                playerToLocation.put(uuid, new ToLocation(location.toBukkitLocation(), event.getDoneMessage()));
            }
        }
    }

    @Subscribe
    public void onSendPlayerToPlayer(SendPlayerToPlayerEvent event) {
        Player toPlayer = Bukkit.getPlayer(event.getToPlayerUUID());
        if (toPlayer == null) {
            return;
        }
        Location location = toPlayer.getLocation();
        for (UUID uuid : event.getSendPlayerUUID()) {
            Player sendPlayer = Bukkit.getPlayer(uuid);
            if (sendPlayer != null) {
                HamsterBallPlugin.sync(() -> {
                    sendPlayer.teleport(location);
                    if (event.getDoneMessage() != null) {
                        Audience audience = CoreAPI.getInstance().getAudienceProvider().player(sendPlayer.getUniqueId());
                        event.getDoneMessage().show(audience, TextReplacementConfig.builder()
                                .matchLiteral("%player_name%")
                                .replacement(toPlayer.getName())
                                .build());
                    }
                    if (event.getDoneTargetMessage() != null) {
                        Audience audience = CoreAPI.getInstance().getAudienceProvider().player(toPlayer.getUniqueId());
                        event.getDoneTargetMessage().show(audience, TextReplacementConfig.builder()
                                .matchLiteral("%player_name%")
                                .replacement(sendPlayer.getName())
                                .build());
                    }
                });
            } else {
                ToPlayer data = new ToPlayer(
                        toPlayer.getUniqueId(),
                        toPlayer.getLocation(),
                        event.getDoneMessage(),
                        event.getDoneTargetMessage()
                );
                playerToPlayer.put(uuid, data);
            }
        }
    }

    @NotNull
    private List<DisplayMessage> getCachedPlayerMessage(@NotNull UUID uuid) throws SQLException {
        ArrayList<DisplayMessage> list = new ArrayList<>();
        try (Connection connection = BallAPI.getInstance().getDatasource().getConnection()) {
            try (PreparedStatement statement = connection.prepareStatement(
                    "SELECT message FROM `hamster_ball_cached_message` WHERE `uuid`=?;"
            )) {
                statement.setString(1, uuid.toString());
                try (ResultSet set = statement.executeQuery()) {
                    while (set.next()) {
                        DisplayMessage msg = CoreAPI.getInstance().getGson().fromJson(set.getString("message"), DisplayMessage.class);
                        list.add(msg);
                    }
                }
            }
        }
        return list;
    }

    private void removeCachedPlayerMessage(@NotNull UUID uuid) throws SQLException {
        try (Connection connection = BallAPI.getInstance().getDatasource().getConnection()) {
            try (PreparedStatement statement = connection.prepareStatement(
                    "DELETE FROM `hamster_ball_cached_message` WHERE `uuid`=?;"
            )) {
                statement.setString(1, uuid.toString());
                statement.executeUpdate();
            }
        }
    }

    @Data
    @AllArgsConstructor
    private static class ToLocation {
        private Location location;
        private DisplayMessage doneMessage;
    }

    @Data
    @AllArgsConstructor
    private static class ToPlayer {
        private UUID toPlayerUUID;
        private Location location;
        private DisplayMessage doneMessage;
        private DisplayMessage doneTargetMessage;
    }
}
